#coding: UTF-8

'''
训练文件
'''

import tensorflow as tf
import numpy as np
import os
import time
import datetime
import random
from simnet_cnn import SimnetCNN
from tensorflow.contrib import learn
from util import *

# Data loading params
tf.flags.DEFINE_float("dev_sample_percentage", .1, "Percentage of the training data to use for validation")
tf.flags.DEFINE_string("train_file", "../data/train.csv", "train data.")
tf.flags.DEFINE_string("test_file", "../data/test.csv", "test data")
tf.flags.DEFINE_string("predict_file", "predict.csv", "predict file")

# Model Hyperparameters
tf.flags.DEFINE_integer("embedding_dim", 128, "Dimensionality of character embedding (default: 128)")
tf.flags.DEFINE_string("filter_sizes", "1,2,3", "Comma-separated filter sizes (default: '3,4,5')")
tf.flags.DEFINE_integer("num_filters", 128, "Number of filters per filter size (default: 128)")
tf.flags.DEFINE_float("dropout_keep_prob", 0.9, "Dropout keep probability (default: 0.5)")
tf.flags.DEFINE_float("l2_reg_lambda", 1.0, "L2 regularization lambda (default: 0.0)")

# Training parameters
tf.flags.DEFINE_integer("batch_size", 32, "Batch Size (default: 64)")
tf.flags.DEFINE_integer("num_epochs", 3, "Number of training epochs (default: 200)")
tf.flags.DEFINE_integer("evaluate_every", 1000, "Evaluate model on dev set after this many steps (default: 100)")
tf.flags.DEFINE_integer("checkpoint_every", 100, "Save model after this many steps (default: 100)")
tf.flags.DEFINE_integer("num_checkpoints", 5, "Number of checkpoints to store (default: 5)")
# Misc Parameters
tf.flags.DEFINE_boolean("allow_soft_placement", True, "Allow device soft device placement")
tf.flags.DEFINE_boolean("log_device_placement", True, "Log placement of ops on devices")

FLAGS = tf.flags.FLAGS
FLAGS._parse_flags()
print("\nParameters:")
for attr, value in sorted(FLAGS.__flags.items()):
    print("{}={}".format(attr.upper(), value))
print("")


from dato import *

print("load_data")
x1, x2, y = load_train_data_label(FLAGS.train_file)
#y = np.array([[(float(i) - 0.5)*2.0] for i in y])
y = np.array([[int(i)] for i in y])

max_document_length = max (
    max( [len(i.split(" ")) for i in x1] ), \
    max( [len(i.split(" ")) for i in x2] )
)
#sys.exit(1)

#手工制定
max_document_length = 50
print("max_document_length", max_document_length)

vocab_processor = learn.preprocessing.VocabularyProcessor(max_document_length)



x_total = x1[:]
x_total.extend(x2)

print "len x1", len(x1)
print "len x2", len(x2)
print "len xtotal", len(x_total)
print "learn vocab"
vocab_processor.fit(x_total)
x1_seq = np.array(list(vocab_processor.transform(x1)))
x2_seq = np.array(list(vocab_processor.transform(x2)))

#应该随机打乱的，这个暂时先不要了
x1_shuffled = x1_seq
x2_shuffled = x2_seq
y_shuffled = y


#分割数据集，这个还是要得
print "split dataset"
dev_sample_index = -1 * int(FLAGS.dev_sample_percentage * float(len(y)))
x1_train, x1_dev = x1_shuffled[:dev_sample_index], x1_shuffled[dev_sample_index:]
x2_train, x2_dev = x2_shuffled[:dev_sample_index], x2_shuffled[dev_sample_index:]
y_train, y_dev = y_shuffled[:dev_sample_index], y_shuffled[dev_sample_index:]
x1_text_train, x1_text_dev = x1[:dev_sample_index], x1[dev_sample_index:]
x2_text_train, x2_text_dev = x2[:dev_sample_index], x2[dev_sample_index:]

print "x1_train", len(x1_train)
print "x1_dev", len(x1_dev)

#训练
graph = tf.Graph()
with graph.as_default():
    session_conf = tf.ConfigProto(
      allow_soft_placement=FLAGS.allow_soft_placement,
      log_device_placement=FLAGS.log_device_placement)
    sess = tf.Session(config=session_conf)
    with sess.as_default():
        simnet = SimnetCNN(
            sequence_length = x1_train.shape[1],
            vocab_size = len(vocab_processor.vocabulary_),
            embedding_size = FLAGS.embedding_dim, 
            filter_sizes=list(map(int, FLAGS.filter_sizes.split(","))),
            num_filters=FLAGS.num_filters,
            l2_reg_lambda=FLAGS.l2_reg_lambda, 
            batch_size = FLAGS.batch_size
        )

        global_step = tf.Variable(0, name="global_step", trainable=False)
        #optimizer = tf.train.AdamOptimizer(1e-3)
        optimizer = tf.train.MomentumOptimizer(1e-3,0.9)
        #optimizer = tf.train.AdadeltaOptimizer(1e-3)
        #optimizer = tf.train.RMSPropOptimizer(1e-3)
        #optimizer = tf.train.AdagradOptimizer(1e-3)
        grads_and_vars = optimizer.compute_gradients(simnet.loss)
        train_op = optimizer.apply_gradients(grads_and_vars, global_step=global_step)

        grad_summaries = []
        for g, v in grads_and_vars:
            if g is not None:
                grad_hist_summary = tf.summary.histogram("{}/grad/hist".format(v.name), g)
                #sparsity_summary = tf.summary.scalar("{}/grad/sparsity".format(v.name), tf.nn.zero_fraction(g))
                grad_summaries.append(grad_hist_summary)
                #grad_summaries.append(sparsity_summary)
        grad_summaries_merged = tf.summary.merge(grad_summaries)
        # Output directory for models and summaries
        timestamp = str(int(time.time()))
        out_dir = os.path.abspath(os.path.join(os.path.curdir, "runs", timestamp))
        print("Writing to {}\n".format(out_dir))

        # Summaries for loss and accuracy
        loss_summary = tf.summary.scalar("loss", simnet.loss)
        #acc_summary = tf.summary.scalar("accuracy", simnet.accuracy)

        # Train Summaries
        train_summary_op = loss_summary #tf.summary.merge([loss_summary, grad_summaries_merged])
        train_summary_dir = os.path.join(out_dir, "summaries", "train")
        train_summary_writer = tf.summary.FileWriter(train_summary_dir, sess.graph)

        # Dev summaries
        dev_summary_op = tf.summary.merge([loss_summary])
        dev_summary_dir = os.path.join(out_dir, "summaries", "dev")
        dev_summary_writer = tf.summary.FileWriter(dev_summary_dir, sess.graph)
        # Checkpoint directory. Tensorflow assumes this directory already exists so we need to create it
        checkpoint_dir = os.path.abspath(os.path.join(out_dir, "checkpoints"))
        checkpoint_prefix = os.path.join(checkpoint_dir, "model")
        if not os.path.exists(checkpoint_dir):
            os.makedirs(checkpoint_dir)
        saver = tf.train.Saver(tf.global_variables(), max_to_keep=FLAGS.num_checkpoints)

        # Write vocabulary
        vocab_processor.save(os.path.join(out_dir, "vocab"))

        # Initialize all variables
        sess.run(tf.global_variables_initializer())

        def train_step(q1b, q2b, yb):
            """
            A single training step
            """
            #print yb, "sd"
            feed_dict = {
              simnet.q1vec: q1b,
              simnet.q2vec: q2b,
              simnet.input_y: yb,
              simnet.dropout_keep_prob: FLAGS.dropout_keep_prob
            }
            _, step, summaries, loss, scores = sess.run(
                [train_op, global_step, train_summary_op, simnet.loss, simnet.cos_12],
                feed_dict)
            time_str = datetime.datetime.now().isoformat()
            print("{}: step {}, loss {:g}".format(time_str, step, loss))
            train_summary_writer.add_summary(summaries, step)
            #pred = graph.get_operation_by_name("output-zxm/scores-zxm").outputs[0]
            #print pred
            #print scores
            res = {}
            res["loss"] = loss
            return res

        batches = batch_iter(list(zip(x1_train, x2_train, y_train)), \
                             FLAGS.batch_size, FLAGS.num_epochs)
        
        def dev_step(q1b, q2b, yb):
            print "begin dev step"
            total_logloss = 0.0;

            predictions = graph.get_operation_by_name("output-zxm/scores-zxm").outputs[0]
            all_pred = []

            batches_dev = batch_iter(list(zip(x1_dev, x2_dev, y_dev, x1_text_dev, x2_text_dev)), 256, 1, shuffle=False)
            for x_test_batch in batches_dev:
                x1d, x2d, yd, x1td, x2td = zip(*x_test_batch)
                batch_predictions = sess.run(predictions, 
                    {simnet.q1vec:x1d, simnet.q2vec:x2d, simnet.dropout_keep_prob: FLAGS.dropout_keep_prob},
                    )
                all_pred = np.concatenate([all_pred, batch_predictions])

            out_list = []
            out_list1 = []
            m3 = 0
            m5 = 0
            m2 = 0
            m1 = 0
            m7 = 0
            m9 = 0
            for i in range(len(yb)):
                i = int(i)
                tb = yb[i]
                if tb > 0.9:
                    tb = 1
                else :
                    tb = 0
                cha = abs(tb - all_pred[i])
                total_logloss += logloss(tb, all_pred[i])
                if cha > 0.5:
                    l = ["dev-batch-bad-case", x1_text_dev[i], x2_text_dev[i], tb, all_pred[i], cha]
                    l = [str(j) for j in l]
                    out_list.append('\t'.join(l))
                if cha < 0.1:
                    #print x1_text_dev[i]
                    #print x2_text_dev[i]
                    #print tb
                    #print all_pred[i]
                    l = ["dev-batch-good-case", x1_text_dev[i], x2_text_dev[i], tb, all_pred[i], cha]
                    l = [str(j) for j in l]
                    out_list1.append('\t'.join(l))
 
                if (cha < 0.1) :
                    m1 += 1
                if (cha < 0.2) :
                    m2 += 1
                if (cha < 0.3) :
                    m3 += 1
                if (cha < 0.5) :
                    m5 += 1
                if (cha < 0.7) :
                    m7 += 1
                if (cha < 0.9):
                    m9 += 1
            if len(out_list) < 10:
                smp = out_list
            else :
                smp = random.sample(out_list, 10)
            for i in smp:
                print i
            if len(out_list1) < 10:
                smp1 = out_list1
            else:
                smp1 = random.sample(out_list1, 10)
            for i in smp1:
                print i

            print "logloss", total_logloss / len(yb), "badcase个数占比", 1.0 * len(out_list) / len(q1b), \
                           "|" , m1 , m2-m1, m3-m2, m5-m3, m7-m5, m9-m7, len(yb) - m9, \
                           "|", len(out_list), len(out_list1), \
                           "|" , len(yb), len(all_pred)

        num_run = 0
        num_loss = 0.0
        num_epoch = 0
        for batch in batches:
            x1b_w, x2b_w, yb_w = zip(*batch)
            
            res = train_step(x1b_w, x2b_w, yb_w)
            loss = res["loss"] * FLAGS.batch_size
            
            if num_run > len(x1_train):
                print "avg_loss = %s, epoch = %s"%(
                    num_loss / len(x1_train), 
                    num_epoch)
                num_epoch += 1
                num_loss = 0.0
                num_run = 0
                dev_step(x1_dev, x2_dev, y_dev)
            
            num_loss += loss
            num_run += FLAGS.batch_size 
            print "current_avg_loss = %s, epoch = %s , num_run ＝　%s, closs = %s"%(
                    num_loss / num_run, 
                    num_epoch, num_run, loss)

            current_step = tf.train.global_step(sess, global_step)
            #if current_step % FLAGS.evaluate_every == 0:
                
    print "load test data"
    x1_test, x2_test, test_id = load_test_data(FLAGS.test_file)
    x1_seq_test = np.array(list(vocab_processor.transform(x1_test)))
    x2_seq_test = np.array(list(vocab_processor.transform(x2_test)))

    predictions = graph.get_operation_by_name("output-zxm/scores-zxm").outputs[0]
    batches_dev = batch_iter(list(zip(x1_seq_test, x2_seq_test, test_id, x1_test, x2_test)), 256, 1, shuffle=False)
    test_pred = []
    cnt = 0
    for x_test_batch in batches_dev:
        print "iter", cnt
        x1d, x2d, test_idd, x1td, x2td = zip(*x_test_batch)
        batch_predictions = sess.run(predictions, \
            {simnet.q1vec:x1d, simnet.q2vec:x2d, simnet.dropout_keep_prob: FLAGS.dropout_keep_prob},\
        )
        test_pred = np.concatenate([test_pred, batch_predictions])
        cnt += 1
    
    print "write"
    if len(test_id) != len(test_pred):
        print "len not equal"
        sys.exit(1)

    t = open(FLAGS.predict_file, "w")
    t.write("test_id,is_duplicate\n")
    for i in range(len(test_id)):
        t.write("%s,%s\n"%(test_id[i], test_pred[i]))
    t.close()



